Concurrent use of shared resources is commonplace. In antiquity, townspeople lined up at a common well to draw water for their respective families. In modem life, and in particular in the realm of information technology, processors, memory, data, software objects, network connections, and numerous other resources may be shared by many users. Such users may be people, systems, applications or other programs, software objects, processing threads, database transactions, or any other entity that may be configured to draw on a shared resource in some way.
In certain circumstances, it may be critical that at any given time only one user be allowed to use a shared resource in a way that changes the state of the resource. For example, in the case of a shared set of data, such as may be stored in a relational or other database, to preserve the integrity and consistency of the data it may be necessary to ensure that two or more users do not separately read and then subsequently attempt to update the same data value at the same time. To illustrate, in the case of a credit card transaction authorization system, different transactions running on the system may require access to the same data value, such as to verify that an account will not exceed the credit limit if a transaction is completed. Assume a first transaction reads the current balance information from a database and while the first transaction is determining if the first transaction is allowed under the credit limit a second transaction reads the same current balance information and begins to make a similar determination with regard to the second transaction. Suppose the first transaction determines that the transaction should be approved, because it will increase the pending balance to be exactly equal to the credit limit. Under this scenario, the second transaction might be approved, based on the stale current balance data previously read from the database (i.e., before it was updated by the first transaction), resulting in the second transaction being approved when in fact it should have been rejected. Worse still, if the second transaction is permitted to update the current balance value to reflect the previously read (and now stale) current balance data value and the amount of the second transaction, the update made by the first transaction may be lost.
To avoid such problems, both in the context of accessing shared data and in the context of other shared resources, various schemes have been employed. In one typical approach, for example, the second transaction above might not have been permitted to update the current balance data value based on the previously read current balance data that did not reflect the update by the first transaction. In such an approach, the second transaction might fail and have to retry, in which case the updated current balance information (reflecting the first transaction) would be read and used by the second transaction to make its decision. In another typical approach, the first transaction might “lock” one or more data records associated with the current balance information, thereby preventing the second transaction (or any other transaction) from accessing the locked records. Once the first transaction has been completed, the lock is released and the record(s) become available to be locked and used exclusively by the second and/or other transactions.
One potential shortcoming of using locks to provide for exclusive, serial concurrent use of shared resources is that while one user has a resource locked the resource is not available to be used by other users, even if the user holding the lock is not at a given moment actively performing operations using the resource. Under such an approach, transactions locked out of using needed resources may fail and have to be retried more frequently than if locks had not been used. This potential shortcoming may be exacerbated in contexts in which to perform a related set of operations a user may need to employ a plurality of shared resources asynchronously. For example, if a user needs to perform in series a set of operations comprising a first operation using resource A, a second operation using resource B, and a third operation using resource C, the set of operations could fail (and possibly the whole series of operations retried) if any of the resources A, B, and/or C is found to have been locked by another user such that it is not available to the user when needed. In fact, the set of operations could fail repeatedly if on each attempt at least one of the resources is found to have been locked by another user at the time it is needed.
When it may be necessary to use more than one shared resource to perform an operation or a related set or series of operations, as described above, two approaches have been used. In the first, the user may identify all the resources the user will need to perform the operation(s) and then attempt to lock all of the resources in advance. In this way, the user can be sure that each resource will be available to it when needed. The shortcomings of this approach include that the user may lock the resources longer than needed, thereby depriving other users of their use for longer than would otherwise be necessary. Also, this approach requires the user to identify in advance all of the shared resources the user will need, which may require excessive time or effort (computational resources, e.g.) and which may not be possible in all cases (such as where the identity of resource cannot be determined until after on or more operations of the set have been performed. Another typical approach is to have the user lock resources only as they are needed, but this approach suffers from the shortcoming identified above, in that some of the operations may be completed only to find that the related set of operations fails and has to be retried because a lock could not be obtained as required for a subsequent operation of the set.
Therefore, there is a need for a better way to provide concurrent use of shared resources, especially in contexts in which one or more users may need access to a plurality of shared resources to perform an operation or set of related operations.